package net.lookyou.boot.demo.config.security;

import net.lookyou.boot.demo.config.security.entrypoint.CustomerAuthenticationEntryPoint;
import net.lookyou.boot.demo.config.security.filter.PhoneAuthenticationFilter;
import net.lookyou.boot.demo.config.security.filter.PassWordAuthenticationFilter;
import net.lookyou.boot.demo.config.security.handler.CustomerAuthenticationFailureHandler;
import net.lookyou.boot.demo.config.security.handler.CustomerAuthenticationSuccessHandler;
import net.lookyou.boot.demo.config.security.handler.CustomerLogoutSuccessHandler;
import net.lookyou.boot.demo.config.security.provider.PhoneAuthenticationProvider;
import net.lookyou.boot.demo.config.security.provider.PassWordAuthenticationProvider;
import net.lookyou.boot.demo.config.redis.RedisSessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * </p>
 *
 * @Author: hufei
 * @Date: 2021/06/1/21:20
 */
@Configuration
@EnableWebSecurity
@ConfigurationProperties("system")
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableRedisHttpSession(redisNamespace = "spring-security:session",maxInactiveIntervalInSeconds = 7200)
public class SecurityConfig {

    @Resource
    CustomerLogoutSuccessHandler customerLogoutSuccessHandler;
    @Resource
    private PhoneAuthenticationProvider phoneAuthenticationProvider;
    @Resource
    private PassWordAuthenticationProvider passWordAuthenticationProvider;
    @Resource
    private CustomerAuthenticationSuccessHandler customerAuthenticationSuccessHandler;
    @Resource
    private CustomerAuthenticationFailureHandler customerAuthenticationFailureHandler;

    /**
     * 免认证地址
     **/
    private String[] uncheckArray;

    public String[] getUncheckArray() {
        return uncheckArray;
    }

    public void setUncheckArray(String[] uncheckArray){
        this.uncheckArray = uncheckArray;
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    //session注册器
    @Bean("sessionRegistry")
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }


    /**
     * 用于踢人下线
     * @param redisIndexedSessionRepository
     * @param applicationEventPublisher
     * @return
     */
    @Bean
    public RedisSessionService sessionService(RedisIndexedSessionRepository redisIndexedSessionRepository, ApplicationEventPublisher applicationEventPublisher){
        return new RedisSessionService(redisIndexedSessionRepository,applicationEventPublisher);
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        List<AuthenticationProvider> providerList = new ArrayList<>();
        providerList.add(phoneAuthenticationProvider);
        providerList.add(passWordAuthenticationProvider);
        return new ProviderManager(providerList);
    }


    @Bean
    PhoneAuthenticationFilter phoneAuthenticationFilter() throws Exception {
        PhoneAuthenticationFilter phoneAuthenticationFilter = new PhoneAuthenticationFilter();
        phoneAuthenticationFilter.setAuthenticationSuccessHandler(customerAuthenticationSuccessHandler);
        phoneAuthenticationFilter.setAuthenticationFailureHandler(customerAuthenticationFailureHandler);
        phoneAuthenticationFilter.setAuthenticationManager(authenticationManager());
        phoneAuthenticationFilter.setFilterProcessesUrl("/phone-login");
        return phoneAuthenticationFilter;
    }

    @Bean
    PassWordAuthenticationFilter passWordAuthenticationFilter() throws Exception {
        PassWordAuthenticationFilter passWordAuthenticationFilter = new PassWordAuthenticationFilter();
        passWordAuthenticationFilter.setAuthenticationSuccessHandler(customerAuthenticationSuccessHandler);
        passWordAuthenticationFilter.setAuthenticationFailureHandler(customerAuthenticationFailureHandler);
        passWordAuthenticationFilter.setAuthenticationManager(authenticationManager());
        passWordAuthenticationFilter.setFilterProcessesUrl("/password-login");
        return passWordAuthenticationFilter;
    }

    @Resource
    private CustomerAuthenticationEntryPoint customerAuthenticationEntryPoint;

    //@Override
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();

        //不校验的路径
        registry.antMatchers(HttpMethod.GET, uncheckArray).permitAll();
        // 全部需要认证
        registry.anyRequest().authenticated();

        // 禁用CSRF 开启跨域
        http.csrf().disable().cors();
        // 注销设置
        http.logout().logoutSuccessHandler(customerLogoutSuccessHandler);
        //未登陆时访问需要认证的地址时返回
        http.exceptionHandling().authenticationEntryPoint(customerAuthenticationEntryPoint);

        http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true).sessionRegistry(sessionRegistry());
        // 手机号登录
        http.addFilterAt(phoneAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        //http.addFilterBefore(new JwtFilter(), VueLoginAuthenticationFilter.class);
        // 账号密码登录
        http.addFilterAt(passWordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

